package org.skydiveapp;

import java.io.Serializable;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import org.skydiveapplibrary.repository.AircraftRepository;
import org.skydiveapplibrary.data.AltitudeUnit;
import org.skydiveapplibrary.data.FreefallProfileType;
import org.skydiveapplibrary.util.FreefallTimeCalculator;
import org.skydiveapplibrary.repository.LocationRepository;
import org.skydiveapplibrary.data.LogEntry;
import org.skydiveapplibrary.repository.LogEntryRepository;
import org.skydiveapplibrary.repository.RigRepository;
import org.skydiveapplibrary.repository.SkydiveTypeRepository;
import org.skydiveapplibrary.ui.FormatUtility;
import org.skydiveapplibrary.ui.NumberDialog;
import org.skydiveapplibrary.ui.NumberDialog.OnNumberSetListener;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.DatePickerDialog;
import android.app.DatePickerDialog.OnDateSetListener;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.DatePicker;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TableRow;
import android.widget.TextView;

public class LogEntryEditActivity extends Activity
{
	// activity request codes
	private static final int REQUEST_SELECT_SKYDIVE_TYPE = 0;
	private static final int REQUEST_SELECT_LOCATION = 1;
	private static final int REQUEST_SELECT_AIRCRAFT = 2;
	private static final int REQUEST_SELECT_GEAR = 3;
	private static final int REQUEST_DIAGRAM = 4;
	private static final int REQUEST_NOTES = 5;
	
	// repositories
	private LogEntryRepository repository = new LogEntryRepository(this);
	private SkydiveTypeRepository skydiveTypeRepository = new SkydiveTypeRepository(this);
	private LocationRepository locRepository = new LocationRepository(this);
	private AircraftRepository aircraftRepository = new AircraftRepository(this);
	private RigRepository rigRepository = new RigRepository(this);

	// menus
	private MenuItem saveMenu;
	private MenuItem cancelMenu;
	
	// data
	private boolean isEdit = false;
	private boolean isCopy = false;
	private LogEntry logEntry;
	
	// listeners
	private OnClickListener jumpNumberEditListener = new OnClickListener()
	{
		@Override
		public void onClick(View view)
		{
			showJumpNumberDialog();
		}
	};
	private OnNumberSetListener jumpNumberSetListener = new OnNumberSetListener()
	{
		@Override
		public void onNumberSet(NumberDialog dialog, int number)
		{
			updateJumpNumber(number);
		}
	};
	private OnClickListener dateEditListener = new OnClickListener()
	{
		@Override
		public void onClick(View view)
		{
			showDateDialog();
		}
	};
	private OnDateSetListener dateSetListener = new OnDateSetListener()
	{
		@Override
		public void onDateSet(DatePicker dialog, int year, int month, int day)
		{
			Calendar cal = Calendar.getInstance();
			cal.set(year, month, day);
			updateDate(cal.getTime());
		}
	};
	private OnClickListener skydiveTypeEditListener = new OnClickListener()
	{
		@Override
		public void onClick(View view)
		{
			Intent intent = new Intent(LogEntryEditActivity.this, SkydiveTypeSelectListActivity.class);
			if (logEntry.getSkydiveType() != null)
			{
				intent.putExtra(Constants.EXTRA_SKYDIVE_TYPE_ID, logEntry.getSkydiveType().getId());
			}
			startActivityForResult(intent, REQUEST_SELECT_SKYDIVE_TYPE);
		}
	};
	private OnClickListener locationEditListener = new OnClickListener()
	{
		@Override
		public void onClick(View view)
		{
			Intent intent = new Intent(LogEntryEditActivity.this, LocationSelectListActivity.class);
			if (logEntry.getLocation() != null)
			{
				intent.putExtra(Constants.EXTRA_LOCATION_ID, logEntry.getLocation().getId());
			}
			startActivityForResult(intent, REQUEST_SELECT_LOCATION);
		}
	};
	private OnClickListener aircraftEditListener = new OnClickListener()
	{
		@Override
		public void onClick(View view)
		{
			Intent intent = new Intent(LogEntryEditActivity.this, AircraftSelectListActivity.class);
			if (logEntry.getAircraft() != null)
			{
				intent.putExtra(Constants.EXTRA_AIRCRAFT_ID, logEntry.getAircraft().getId());
			}
			startActivityForResult(intent, REQUEST_SELECT_AIRCRAFT);
		}
	};
	private OnClickListener gearEditListener = new OnClickListener()
	{
		@Override
		public void onClick(View view)
		{
			Intent intent = new Intent(LogEntryEditActivity.this, GearListSelectActivity.class);
			intent.putExtra(Constants.EXTRA_SELECTED_RIG_IDS, (Serializable)getSelectedRigIds());
			startActivityForResult(intent, REQUEST_SELECT_GEAR);
		}
	};
	private OnClickListener estimateFFTimeListener = new OnClickListener()
	{
		@Override
		public void onClick(View v)
		{
			estimateFreefallTime();
		}
	};
	private OnClickListener diagramEditListener = new OnClickListener()
	{
		@Override
		public void onClick(View view)
		{
			Intent intent = new Intent(LogEntryEditActivity.this, LogEntryDiagramActivity.class);
			intent.putExtra(Constants.EXTRA_DIAGRAM, logEntry.getDiagram());
			startActivityForResult(intent, REQUEST_DIAGRAM);
		}
	};
	private OnClickListener notesEditListener = new OnClickListener()
	{
		@Override
		public void onClick(View view)
		{
			Intent intent = new Intent(LogEntryEditActivity.this, NotesEditActivity.class);
			intent.putExtra(Constants.EXTRA_NOTES, logEntry.getNotes());
			startActivityForResult(intent, REQUEST_NOTES);
		}
	};
	private OnClickListener deleteListener = new OnClickListener()
	{
		@Override
		public void onClick(View view)
		{
			showDeleteWarning();
		}
	};
	private DialogInterface.OnClickListener deleteConfirmListener = new DialogInterface.OnClickListener()
	{
		@Override
		public void onClick(DialogInterface arg0, int arg1)
		{
			delete();
		}
	};
	
	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		
		// set isEdit and isCopy flag
		Bundle extras = getIntent().getExtras();
		isEdit = (extras != null && extras.containsKey(Constants.EXTRA_LOG_ENTRY_ID));
		isCopy = (extras != null && extras.containsKey(Constants.EXTRA_LOG_ENTRY_COPY));
		
		// load data
		loadData();
		
		// init ui
		initializeUI();
		
		// update fields
		updateFields();
	}
	
	@SuppressWarnings("unchecked")
	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent intent)
	{
		if (requestCode == REQUEST_SELECT_SKYDIVE_TYPE && resultCode == RESULT_OK)
		{
			int id = intent.getIntExtra(Constants.EXTRA_SKYDIVE_TYPE_ID, -1);
			updateSkydiveType(id);
		}
		else if (requestCode == REQUEST_SELECT_LOCATION && resultCode == RESULT_OK)
		{
			int id = intent.getIntExtra(Constants.EXTRA_LOCATION_ID, -1);
			updateLocation(id);
		}
		else if (requestCode == REQUEST_SELECT_AIRCRAFT && resultCode == RESULT_OK)
		{
			int id = intent.getIntExtra(Constants.EXTRA_AIRCRAFT_ID, -1);
			updateAircraft(id);
		}
		else if (requestCode == REQUEST_SELECT_GEAR && resultCode == RESULT_OK)
		{
			List<Integer> ids = (List<Integer>)intent.getSerializableExtra(Constants.EXTRA_SELECTED_RIG_IDS);
			updateRig(ids);
		}
		else if (requestCode == REQUEST_NOTES && resultCode == RESULT_OK)
		{
			String notes = intent.getStringExtra(Constants.EXTRA_NOTES);
			updateNotes(notes);
		}
		else if (requestCode == REQUEST_DIAGRAM && resultCode == RESULT_OK)
		{
			byte[] data = intent.getByteArrayExtra(Constants.EXTRA_DIAGRAM);
			updateDiagram(data);
		}
	}
	
	private void loadData()
	{
		// determine add vs. edit
		if (isEdit)
		{
			// editing
			int logEntryId = getIntent().getExtras().getInt(Constants.EXTRA_LOG_ENTRY_ID);
			logEntry = repository.getLogEntry(logEntryId);
		}
		else if (isCopy)
		{
			// create new using last logs fields
			logEntry = repository.getNewFromLastEntry();
		}
		else
		{
			// adding
			logEntry = new LogEntry();
			// set defaults
			repository.setDefaults(logEntry);
		}
	}
	
	private void initializeUI()
	{
		// show view
		setContentView(R.layout.log_entry_edit_view);
		
		// set header text
		TextView headerTextView = (TextView)findViewById(R.id.EditHeaderTextView);
		headerTextView.setText(isEdit ?
				R.string.log_entry_edit_label : R.string.log_entry_new_label);
		
		// set jump number listener
		TextView jumpNumberField = (TextView)findViewById(R.id.LogEntryJumpNumberField);
		jumpNumberField.setOnClickListener(jumpNumberEditListener);
		
		// set date listener
		TextView dateField = (TextView)findViewById(R.id.LogEntryDateField);
		dateField.setOnClickListener(dateEditListener);
		
		// set type listener
		TextView typeField = (TextView)findViewById(R.id.LogEntryTypeField);
		typeField.setOnClickListener(skydiveTypeEditListener);
		
		// set location listener
		TextView locField = (TextView)findViewById(R.id.LogEntryLocationField);
		locField.setOnClickListener(locationEditListener);
		
		// set aircraft listener
		TextView airField = (TextView)findViewById(R.id.LogEntryAircraftField);
		airField.setOnClickListener(aircraftEditListener);
		
		// set gear listener
		TextView gearField = (TextView)findViewById(R.id.LogEntryGearField);
		gearField.setOnClickListener(gearEditListener);
		
		// set exit altitude label
		TextView eaLabel = (TextView)findViewById(R.id.LogEntryExitAltitudeLabel);
		eaLabel.setText(MessageFormat.format(
				getString(R.string.log_entry_exit_altitude_label_format),
				getString(logEntry.getAltitudeUnit().getResourceId())));
			
		// set deply altitude label
		TextView daLabel = (TextView)findViewById(R.id.LogEntryDeploymentAltitudeLabel);
		daLabel.setText(MessageFormat.format(
				getString(R.string.log_entry_deployment_altitude_label_format),
				getString(logEntry.getAltitudeUnit().getResourceId())));
			
		// set diagram listener
		TableRow diRow = (TableRow)findViewById(R.id.LogEntryDiagramRow);
		diRow.setOnClickListener(diagramEditListener);
				
		// set notes listener
		TextView nField = (TextView)findViewById(R.id.LogEntryNotesField);
		nField.setOnClickListener(notesEditListener);
		
		Button deleteButton = (Button)findViewById(R.id.LogEntryDeleteButton);
		deleteButton.setOnClickListener(deleteListener);
		
		View estimateFFTimeButton = findViewById(R.id.LogEntryEstimateFreefallTimeButton);
		estimateFFTimeButton.setOnClickListener(estimateFFTimeListener);
		
		// show/hide delete button
		deleteButton.setVisibility(isEdit ? View.VISIBLE : View.GONE);
	}

	private void updateFields()
	{
		
		// set jump number
		TextView jumpNumberField = (TextView)findViewById(R.id.LogEntryJumpNumberField);
		jumpNumberField.setText(FormatUtility.toNumericString(logEntry.getJumpNumber()));
		
		// set date
		TextView dateField = (TextView)findViewById(R.id.LogEntryDateField);
		dateField.setText(FormatUtility.toDateString(this, logEntry.getDate()));
		
		// set type
		TextView typeField = (TextView)findViewById(R.id.LogEntryTypeField);
		typeField.setText(logEntry.getSkydiveType() == null ? "" :
			logEntry.getSkydiveType().getName());
		
		// set location
		TextView locField = (TextView)findViewById(R.id.LogEntryLocationField);
		locField.setText(logEntry.getLocation() == null ? "" :
			logEntry.getLocation().getName());
		
		// set aircraft
		TextView airField = (TextView)findViewById(R.id.LogEntryAircraftField);
		airField.setText(logEntry.getAircraft() == null ? "" :
			logEntry.getAircraft().getName());
		
		// set gear
		TextView gearField = (TextView)findViewById(R.id.LogEntryGearField);
		gearField.setText(getGearText());
		
		// init exit altitude
		if (logEntry.getExitAltitude() != null)
		{
			EditText eaField = (EditText)findViewById(R.id.LogEntryExitAltitudeField);
			eaField.setText(FormatUtility.toNumericString(logEntry.getExitAltitude()));
		}
		
		// init deployment altitude
		if (logEntry.getDeploymentAltitude() != null)
		{
			EditText daField = (EditText)findViewById(R.id.LogEntryDeploymentAltitudeField);
			daField.setText(FormatUtility.toNumericString(logEntry.getDeploymentAltitude()));
		}

		// set freefall time
		if (logEntry.getFreeFallTimeSeconds() != null)
		{
			EditText ffField = (EditText)findViewById(R.id.LogEntryFreefallTimeField);
			ffField.setText(FormatUtility.toNumericString(logEntry.getFreeFallTimeSeconds()));
		}
		
		// set cutaway
		CheckBox caField = (CheckBox)findViewById(R.id.LogEntryCutawayField);
		caField.setChecked(logEntry.isCutaway());
		
		// set diagram
		ImageView diField = (ImageView)findViewById(R.id.LogEntryDiagramField);
		if (logEntry.getDiagram() != null)
		{
			Bitmap bitmap = BitmapFactory.decodeByteArray(logEntry.getDiagram(), 0, logEntry.getDiagram().length);
			diField.setImageBitmap(bitmap);
		}
		
		// set notes
		TextView nField = (TextView)findViewById(R.id.LogEntryNotesField);
		nField.setText(logEntry.getNotes());
		
		// set signature
		View signatureGroup = findViewById(R.id.LogEntrySignatureGroup);
		if (logEntry.getSignature() == null)
		{
			signatureGroup.setVisibility(View.GONE);
		}
		else
		{
			signatureGroup.setVisibility(View.VISIBLE);
			// set signature image
			Bitmap bitmap = BitmapFactory.decodeByteArray(logEntry.getSignature().getImage(), 0, logEntry.getSignature().getImage().length);
			ImageView signatureField = (ImageView)findViewById(R.id.LogEntrySignatureField);
			signatureField.setImageBitmap(bitmap);
			// set license
			TextView licenseField = (TextView)findViewById(R.id.LogEntryLicenseField);
			licenseField.setText(logEntry.getSignature().getLicense());
		}
	}

	private void showJumpNumberDialog()
	{
		// init dialog
		NumberDialog dialog = new NumberDialog(
				this,
				R.string.jump_number_title,
				logEntry.getJumpNumber(),
				jumpNumberSetListener);
		// show dialog
		dialog.show();
	}
	
	private void showDateDialog()
	{
		// create date dialog
		Calendar cal = Calendar.getInstance();
		cal.setTime(logEntry.getDate());
		DatePickerDialog dialog = new DatePickerDialog(
				this,
				dateSetListener,
				cal.get(Calendar.YEAR),
				cal.get(Calendar.MONTH),
				cal.get(Calendar.DAY_OF_MONTH));
		// show dialog
		dialog.show();
	}
	
	private void showDeleteWarning()
	{
		// create alert dialog
		AlertDialog.Builder builder = new AlertDialog.Builder(this);
		builder.setMessage(R.string.log_entry_delete_warning);
		builder.setIcon(android.R.drawable.ic_dialog_alert);
		builder.setTitle(R.string.delete_confirmation);
		builder.setPositiveButton(R.string.yes_button, deleteConfirmListener);
		builder.setNegativeButton(R.string.no_button, null);
		AlertDialog dialog = builder.create();
		// show dialog
		dialog.show();
	}
	
	private void updateJumpNumber(int jumpNumber)
	{
		// update log entry
		logEntry.setJumpNumber(jumpNumber);
		
		// update ui
		TextView jnField = (TextView)findViewById(R.id.LogEntryJumpNumberField);
		jnField.setText(FormatUtility.toNumericString(logEntry.getJumpNumber()));
	}
	
	private void updateDate(Date date) 
	{
		// update log entry
		logEntry.setDate(date);
		
		// update ui
		TextView dateField = (TextView)findViewById(R.id.LogEntryDateField);
		dateField.setText(FormatUtility.toDateString(this, logEntry.getDate()));
	}
	
	private void updateSkydiveType(int id)
	{
		// update log entry
		logEntry.setSkydiveType(skydiveTypeRepository.getSkydiveType(id));
		
		// update ui
		TextView typeField = (TextView)findViewById(R.id.LogEntryTypeField);
		typeField.setText(logEntry.getSkydiveType() == null ? "" :
			logEntry.getSkydiveType().getName());
	}
	
	private void updateLocation(int id)
	{
		// update log entry
		logEntry.setLocation(locRepository.getLocation(id));
		
		// update ui
		TextView locField = (TextView)findViewById(R.id.LogEntryLocationField);
		locField.setText(logEntry.getLocation() == null ? "" :
			logEntry.getLocation().getName());
	}
	
	private void updateAircraft(int id)
	{
		// update log entry
		logEntry.setAircraft(aircraftRepository.getAircraft(id));
		
		// update ui
		TextView airField = (TextView)findViewById(R.id.LogEntryAircraftField);
		airField.setText(logEntry.getAircraft() == null ? "" :
			logEntry.getAircraft().getName());
	}
	
	private void updateRig(List<Integer> ids)
	{
		// update log entry
		logEntry.getRigs().clear();
		logEntry.getRigs().addAll(rigRepository.getRigs(ids));
		
		// update ui
		TextView gearField = (TextView)findViewById(R.id.LogEntryGearField);
		gearField.setText(getGearText());
	}
	
	private void estimateFreefallTime()
	{
		// get fields
		EditText exitAltField = (EditText)findViewById(R.id.LogEntryExitAltitudeField);
		EditText deplAltField = (EditText)findViewById(R.id.LogEntryDeploymentAltitudeField);
		
		// get values
		FreefallProfileType freefallProfile =
			(logEntry.getSkydiveType() != null && logEntry.getSkydiveType().getFreefallProfileType() != null) ?
					logEntry.getSkydiveType().getFreefallProfileType() : FreefallProfileType.Horizontal;
		AltitudeUnit altUnit = logEntry.getAltitudeUnit();
		int exitAlt = FormatUtility.fromNumericString(exitAltField.getText().toString());
		int deplAlt = FormatUtility.fromNumericString(deplAltField.getText().toString());
		
		// get estimate
		int estimate = FreefallTimeCalculator.calculateFreefallTime(
				freefallProfile, exitAlt, deplAlt, altUnit);
		
		// update ui
		TextView ffTimeField = (TextView)findViewById(R.id.LogEntryFreefallTimeField);
		ffTimeField.setText(String.valueOf(estimate));
	}

	private void updateNotes(String notes)
	{
		// update log entry
		logEntry.setNotes(notes);
		
		// update ui
		TextView nField = (TextView)findViewById(R.id.LogEntryNotesField);
		nField.setText(logEntry.getNotes());
	}
	
	private void updateDiagram(byte[] data)
	{
		// update log entry
		logEntry.setDiagram(data);
		
		// get bitmap
		Bitmap bitmap = null;
		if (logEntry.getDiagram() != null)
		{
			bitmap = BitmapFactory.decodeByteArray(logEntry.getDiagram(), 0, logEntry.getDiagram().length);
		}
		
		// update UI
		ImageView diField = (ImageView)findViewById(R.id.LogEntryDiagramField);
		diField.setImageBitmap(bitmap);
	}
	
	private List<Integer> getSelectedRigIds()
	{
		List<Integer> ids = new ArrayList<Integer>();
		for (int i = 0; i < logEntry.getRigs().size(); i++)
		{
			ids.add(logEntry.getRigs().get(i).getId());
		}
		return ids;
	}
	
	private String getGearText()
	{
		StringBuilder builder = new StringBuilder();
		for (int i = 0; i < logEntry.getRigs().size(); i++)
		{
			builder.append(logEntry.getRigs().get(i).getName());
			if (i < logEntry.getRigs().size() - 1)
			{
				builder.append(", ");
			}
		}
		return builder.toString();
	}
	
	private void save()
	{
		// set exit altitude
		EditText eaField = (EditText)findViewById(R.id.LogEntryExitAltitudeField);
		String eaString = eaField.getText().toString();
		logEntry.setExitAltitude(FormatUtility.fromNumericString(eaString));
		
		// set deployment altitude
		EditText daField = (EditText)findViewById(R.id.LogEntryDeploymentAltitudeField);
		String daString = daField.getText().toString();
		logEntry.setDeploymentAltitude(FormatUtility.fromNumericString(daString));
		
		// set freefall time
		EditText ffField = (EditText)findViewById(R.id.LogEntryFreefallTimeField);
		String ffString = ffField.getText().toString();
		logEntry.setFreeFallTimeSeconds(FormatUtility.fromNumericString(ffString));
		
		// set cutaway
		CheckBox caField = (CheckBox)findViewById(R.id.LogEntryCutawayField);
		logEntry.setCutaway(caField.isChecked());
		
		// save
		if (isEdit)
		{
			repository.updateLogEntry(logEntry);
		}
		else
		{
			repository.addLogEntry(logEntry);
		}
		
		// finish
		finish();
	}
	
	private void delete()
	{
		// delete log entry
		repository.deleteLogEntry(logEntry);
		
		// finish
		finish();
	}
	
	@Override
	public void onBackPressed()
	{
		save();
		super.onBackPressed();
	}
	
	@Override
	public boolean onCreateOptionsMenu(Menu menu)
	{
		cancelMenu = menu.add(R.string.cancel_button);
		cancelMenu.setIcon(R.drawable.ic_menu_revert);
		
		saveMenu = menu.add(R.string.save_button);
		saveMenu.setIcon(R.drawable.ic_menu_save);
		
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item)
	{
		if (item == saveMenu)
		{
        	save();
    		return true;
		}
		else if (item == cancelMenu)
		{
			finish();
    		return true;
		}
		else
		{
			return super.onOptionsItemSelected(item);
		}
	}
}
